using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;

namespace JGSY.CourseManagement.Infrastructure.Messaging
{
    /// <summary>
    /// 实现基于发布/订阅模式的消息总线，支持跨组件通信
    /// </summary>
    public class MessageBus : IMessageBus, IDisposable
    {
        /// <summary>
        /// 封装特定消息类型的订阅信息，包含处理程序和过滤条件
        /// </summary>
        /// <typeparam name="TMessage">消息类型</typeparam>
        private class Subscription<TMessage>
        {
            public Action<TMessage> SyncHandler { get; set; }
            public Func<TMessage, Task> AsyncHandler { get; set; }
            public Predicate<TMessage>? Filter { get; set; }
        }

        private readonly ConcurrentDictionary<Type, ConcurrentBag<object>> _subscribers = new ConcurrentDictionary<Type, ConcurrentBag<object>>();
        private bool _disposed = false;

        /// <summary>
        /// 订阅指定类型的消息（同步处理）
        /// </summary>
        /// <typeparam name="TMessage">要订阅的消息类型</typeparam>
        /// <param name="handler">处理消息的同步委托</param>
        /// <exception cref="ArgumentNullException">当handler为null时抛出</exception>
        public void Subscribe<TMessage>(Action<TMessage> handler)
        {
            Subscribe(handler, null);
        }

        /// <summary>
        /// 订阅指定类型的消息（同步处理），支持消息过滤
        /// </summary>
        /// <typeparam name="TMessage">要订阅的消息类型</typeparam>
        /// <param name="handler">处理消息的同步委托</param>
        /// <param name="filter">可选的消息过滤条件，仅满足条件的消息会被处理</param>
        /// <exception cref="ArgumentNullException">当handler为null时抛出</exception>
        public void Subscribe<TMessage>(Action<TMessage> handler, Predicate<TMessage> filter = null)
        {
            if (handler == null)
                throw new ArgumentNullException(nameof(handler));

            var messageType = typeof(TMessage);
            var subscribers = _subscribers.GetOrAdd(messageType, _ => new ConcurrentBag<object>());
            subscribers.Add(new Subscription<TMessage> { SyncHandler = handler, Filter = filter });
        }

        /// <summary>
        /// 订阅指定类型的消息（异步处理）
        /// </summary>
        /// <typeparam name="TMessage">要订阅的消息类型</typeparam>
        /// <param name="asyncHandler">处理消息的异步委托</param>
        /// <exception cref="ArgumentNullException">当asyncHandler为null时抛出</exception>
        public void Subscribe<TMessage>(Func<TMessage, Task> asyncHandler)
        {
            Subscribe(asyncHandler, null);
        }

        /// <summary>
        /// 订阅指定类型的消息（异步处理），支持消息过滤
        /// </summary>
        /// <typeparam name="TMessage">要订阅的消息类型</typeparam>
        /// <param name="asyncHandler">处理消息的异步委托</param>
        /// <param name="filter">可选的消息过滤条件，仅满足条件的消息会被处理</param>
        /// <exception cref="ArgumentNullException">当asyncHandler为null时抛出</exception>
        public void Subscribe<TMessage>(Func<TMessage, Task> asyncHandler, Predicate<TMessage> filter = null)
        {
            if (asyncHandler == null)
                throw new ArgumentNullException(nameof(asyncHandler));

            var messageType = typeof(TMessage);
            var subscribers = _subscribers.GetOrAdd(messageType, _ => new ConcurrentBag<object>());
            subscribers.Add(new Subscription<TMessage> { AsyncHandler = asyncHandler, Filter = filter });
        }

        /// <summary>
        /// 同步发布消息到所有匹配的订阅者
        /// </summary>
        /// <typeparam name="TMessage">消息类型</typeparam>
        /// <param name="message">要发布的消息实例</param>
        /// <exception cref="ArgumentNullException">当message为null时抛出</exception>
        /// <remarks>异步处理程序将以fire-and-forget方式执行，不会阻塞发布线程</remarks>
        public void Publish<TMessage>(TMessage message)
        {
            if (message == null)
                throw new ArgumentNullException(nameof(message));

            var messageType = typeof(TMessage);
            if (_subscribers.TryGetValue(messageType, out var subscribers))
            {
                foreach (var subscriber in subscribers)
                {
                    switch (subscriber)
                    {
                        case Subscription<TMessage> subscription:
                            if (subscription.Filter == null || subscription.Filter(message))
                            {
                                subscription.SyncHandler?.Invoke(message);
                                if (subscription.AsyncHandler != null)
                                {
                                    _ = subscription.AsyncHandler(message).ConfigureAwait(false);
                                }
                            }
                            break;
                    }
                }
            }
        }

        /// <summary>
        /// 异步发布消息到所有匹配的订阅者
        /// </summary>
        /// <typeparam name="TMessage">消息类型</typeparam>
        /// <param name="message">要发布的消息实例</param>
        /// <returns>表示异步发布操作的任务</returns>
        /// <exception cref="ArgumentNullException">当message为null时抛出</exception>
        /// <remarks>异步处理程序将被等待执行，确保发布完成后再继续</remarks>
        public async Task PublishAsync<TMessage>(TMessage message)
        {
            if (message == null)
                throw new ArgumentNullException(nameof(message));

            var messageType = typeof(TMessage);
            if (_subscribers.TryGetValue(messageType, out var subscribers))
            {
                foreach (var subscriber in subscribers)
                {
                    switch (subscriber)
                    {
                        case Subscription<TMessage> subscription:
                            if (subscription.Filter == null || subscription.Filter(message))
                            {
                                subscription.SyncHandler?.Invoke(message);
                                if (subscription.AsyncHandler != null)
                                {
                                    await subscription.AsyncHandler(message).ConfigureAwait(false);
                                }
                            }
                            break;
                    }
                }
            }
        }

        /// <summary>
        /// 取消订阅指定类型的同步消息处理程序
        /// </summary>
        /// <typeparam name="TMessage">消息类型</typeparam>
        /// <param name="handler">要移除的同步处理程序委托</param>
        /// <exception cref="ArgumentNullException">当handler为null时抛出</exception>
        /// <remarks>仅移除与handler引用完全匹配的订阅</remarks>
        public void Unsubscribe<TMessage>(Action<TMessage> handler)
        {
            if (handler == null)
                throw new ArgumentNullException(nameof(handler));

            var messageType = typeof(TMessage);
            if (_subscribers.TryGetValue(messageType, out var subscribers))
            {
                var newSubscribers = new ConcurrentBag<object>();
                foreach (var subscriber in subscribers)
                {
                    if (subscriber is Subscription<TMessage> subscription)
                    {
                        if (!ReferenceEquals(subscription.SyncHandler, handler))
                        {
                            newSubscribers.Add(subscriber);
                        }
                    }
                    else if (!ReferenceEquals(subscriber, handler))
                    {
                        newSubscribers.Add(subscriber);
                    }
                }
                _subscribers[messageType] = newSubscribers;
            }
        }

        /// <summary>
        /// 取消订阅指定类型的异步消息处理程序
        /// </summary>
        /// <typeparam name="TMessage">消息类型</typeparam>
        /// <param name="asyncHandler">要移除的异步处理程序委托</param>
        /// <exception cref="ArgumentNullException">当asyncHandler为null时抛出</exception>
        /// <remarks>仅移除与asyncHandler引用完全匹配的订阅</remarks>
        public void Unsubscribe<TMessage>(Func<TMessage, Task> asyncHandler)
        {
            if (asyncHandler == null)
                throw new ArgumentNullException(nameof(asyncHandler));

            var messageType = typeof(TMessage);
            if (_subscribers.TryGetValue(messageType, out var subscribers))
            {
                var newSubscribers = new ConcurrentBag<object>();
                foreach (var subscriber in subscribers)
                {
                    if (subscriber is Subscription<TMessage> subscription)
                    {
                        if (!ReferenceEquals(subscription.AsyncHandler, asyncHandler))
                        {
                            newSubscribers.Add(subscriber);
                        }
                    }
                    else if (!ReferenceEquals(subscriber, asyncHandler))
                    {
                        newSubscribers.Add(subscriber);
                    }
                }
                _subscribers[messageType] = newSubscribers;
            }
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (_disposed)
                return;

            if (disposing)
            {
                _subscribers.Clear();
            }

            _disposed = true;
        }



        ~MessageBus()
        {
            Dispose(false);
        }
    }
}